﻿using System;
using NUnit.Framework;

namespace Duhking.Tests
{
    [TestFixture]
    public class MethodInfoComparerTests
    {
        private class ComparerClass
        {
            public void TestVoidMethod() { }

            public void TestMethodWithParameters(string one, string two, string three) { }

            public void TestDifferentReturnType() { }

            public void TestGenericMethod<T>() {}

            public void UnmatchedOutParam(out string param)
            {
                param = string.Empty;
            }

            public void UnmatchedRefParam(ref string param) { }

            public void MathcedRefParam(ref string param) { }
        }

        private MethodInfoComparer _comparer;
        
        [SetUp]
        public void Setup()
        {
            _comparer = new MethodInfoComparer();
        }

        [Test]
        public void MethodInfo_Are_Equal_If_MethodNames_Match()
        {
            var methodX = GetType().GetMethod("TestVoidMethod");
            var methodY = typeof(ComparerClass).GetMethod("TestVoidMethod");
            Assert.That(_comparer.Equals(methodX,methodY), Is.True);
        }

        [Test]
        public void Methods_Are_Equal_If_They_Have_Same_Number_And_Type_Of_Parameters()
        {
            var methodX = GetType().GetMethod("TestMethodWithParameters",new[] { typeof(string),typeof(string),typeof(string)});
            var methodY = typeof(ComparerClass).GetMethod("TestMethodWithParameters");
            Assert.That(_comparer.Equals(methodX,methodY), Is.True);
        }

        [Test]
        public void Methods_Are_Not_Equal_If_Parameter_Numbers_Differ()
        {
            var methodX = GetType().GetMethod("TestMethodWithParameters", new[] { typeof(string), typeof(string), typeof(string), typeof(string) });
            var methodY = typeof(ComparerClass).GetMethod("TestMethodWithParameters");
            Assert.That(_comparer.Equals(methodX, methodY), Is.False);
        }

        [Test]
        public void Methods_Are_Not_Equal_If_Return_Types_Are_Different()
        {
            var methodX = GetType().GetMethod("TestDifferentReturnType");
            var methodY = typeof(ComparerClass).GetMethod("TestDifferentReturnType");
            Assert.That(_comparer.Equals(methodX, methodY), Is.False);
        }

        [Test]
        public void Methods_Are_Not_Equal_If_One_Is_Generic_And_The_Other_Is_Not()
        {
            var methodX = GetType().GetMethod("TestGenericMethod");
            var methodY = typeof(ComparerClass).GetMethod("TestGenericMethod");
            Assert.That(methodY, Is.Not.Null);
            Assert.That(_comparer.Equals(methodX, methodY), Is.False);
        }

        [Test]
        public void Methods_Are_Not_Equal_If_One_Parameter_Is_Out_And_The_Other_Is_Not()
        {
            var methodX = GetType().GetMethod("UnmatchedOutParam");
            var methodY = typeof(ComparerClass).GetMethod("UnmatchedOutParam");
            Assert.That(_comparer.Equals(methodX, methodY), Is.False);
        }

        [Test]
        public void Methods_Are_Not_Equal_If_One_Parameter_Is_Ref_And_The_Other_Is_Not()
        {
            var methodX = GetType().GetMethod("UnmatchedRefParam");
            var methodY = typeof(ComparerClass).GetMethod("UnmatchedRefParam");
            Assert.That(_comparer.Equals(methodX, methodY), Is.False);
        }

        [Test]
        public void Method_Are_Equal_If_Ref_Parameters_Match()
        {
            var methodX = GetType().GetMethod("MathcedRefParam");
            var methodY = typeof(ComparerClass).GetMethod("MathcedRefParam");
            Assert.That(_comparer.Equals(methodX,methodY), Is.True);
        }

        [Test]
        public void GetHashCode_Returns_Hash_Code_From_MethodInfo()
        {
            var methodX = GetType().GetMethod("MathcedRefParam");
            Assert.AreEqual(_comparer.GetHashCode(methodX), methodX.GetHashCode());
        }

        #region Methods for Testing
        public void TestVoidMethod() { }

        public void TestMethodWithParameters(string param1, string param2, string param3) { }

        public void TestMethodWithParameters(string param1, string param2, string param3, string param4) { }

        public int TestDifferentReturnType()
        {
            return default(int);
        }

        public void TestGenericMethod() {}

        public void UnmatchedOutParam(string param1) { }

        public void UnmatchedRefParam(string param1) { }

        public void MathcedRefParam(ref string param) { }

        #endregion
    }
}
